/**
 * 
 */
package ro.dta.idbi.ui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;

import ro.dta.idbi.model.AutoMedUtils;
import ro.dta.idbi.model.QueryResult;
import uk.ac.bbk.dcs.automed.qproc.QProcException;
import uk.ac.bbk.dcs.automed.qproc.iql.ASG;
import uk.ac.bbk.dcs.automed.qproc.iql.ASGResult;
import uk.ac.bbk.dcs.automed.qproc.iql.Cell;
import uk.ac.bbk.dcs.automed.qproc.iql.ParseException;
import uk.ac.ic.doc.automed.IntegrityException;
import uk.ac.ic.doc.automed.reps.Schema;

/**
 * Query results table, allowing the user to view batches of results returned by
 * the IQL query
 * 
 * @author Tudor Dobrila
 * 
 */
public class QueryResultsTable extends AbstractTable {

	private static final long serialVersionUID = 5844329875138717496L;

	/**
	 * True if query should be executed incrementally, false otherwise
	 */
	private boolean executeIncrementally = false;

	/**
	 * Query executed, whose results are displayed in the table
	 */
	private String query;

	/**
	 * Schema over which the query is executed
	 */
	private Schema schema;

	/**
	 * Result of the execution of the query
	 */
	private QueryResult result;

	/**
	 * Loading label, used to show a loading message while processing the query
	 */
	private JLabel loadingLabel;

	/**
	 * True if the query has been changed, false otherwise
	 */
	private boolean queryChanged;

	/**
	 * Results table class constructor
	 * 
	 * @param schema
	 *            Schema over which the query is executed
	 */
	public QueryResultsTable(Schema schema) {
		this.schema = schema;
		pageLabel.setVisible(false);
		backBtn.setVisible(false);
		loadingLabel = new JLabel();
		toolBar.add(loadingLabel);
	}

	/**
	 * Results table class constructor
	 * 
	 * @param schema
	 *            Schema over which the query is executed
	 * @param query
	 *            Initial query to be executed
	 */
	public QueryResultsTable(Schema schema, String query) {
		this(schema);
		setQuery(query);
	}

	@Override
	public int getTotalResults() {
		return Integer.MAX_VALUE;
	}

	@Override
	public int getCurrentResults() {
		return Integer.parseInt(countComboBox.getSelectedItem().toString());
	}

	@Override
	public void refreshTable() {
		super.refreshTable();
		nextBtn.setEnabled(true);

		if (queryChanged) {
			try {
				if (executeIncrementally) {
					result = AutoMedUtils.runIqlQueryIncrementally(schema, query, resultsShown);
				} else {
					result = AutoMedUtils.runIqlQuery(schema, query);
				}
				queryChanged = false;
			} catch (ParseException e) {
				JOptionPane.showMessageDialog(this, "Could not parse the query " + query
						+ ". Reason:\r\n" + e.getMessage(), "Error!", JOptionPane.ERROR_MESSAGE);
			} catch (QProcException e) {
				JOptionPane.showMessageDialog(this, "Could not process the query " + query
						+ ". Reason:\r\n" + e.getMessage(), "Error!", JOptionPane.ERROR_MESSAGE);
			} catch (IntegrityException e) {
				JOptionPane.showMessageDialog(this, "Could not execute the query " + query
						+ ". Reason:\r\n" + e.getMessage(), "Error!", JOptionPane.ERROR_MESSAGE);
			}
		}

		new IqlQueryWorker().execute();
	}

	/**
	 * @return The query currently being executed and displayed in the table
	 */
	public String getQuery() {
		return query;
	}

	/**
	 * @param query
	 *            New query currently being executed and displayed in the table
	 */
	public void setQuery(String query) {
		this.query = query;
		this.queryChanged = true;
		fillTable();
	}

	/**
	 * @return the schema
	 */
	public Schema getSchema() {
		return schema;
	}

	/**
	 * @param schema
	 *            the schema to set
	 */
	public void setSchema(Schema schema) {
		this.schema = schema;
	}

	/**
	 * @return True if query is executed incrementally, false otherwise
	 */
	public boolean isExecuteIncrementally() {
		return executeIncrementally;
	}

	/**
	 * @param executeIncrementally True if query is executed incrementally, false otherwise
	 */
	public void setExecuteIncrementally(boolean executeIncrementally) {
		this.executeIncrementally = executeIncrementally;
	}

	/**
	 * Iql query worked, used to retrieve the next batch of results of an IQL
	 * query in a worker thread and display them to the user
	 * 
	 * @author Tudor Dobrila
	 * 
	 */
	class IqlQueryWorker extends SwingWorker<Integer, Cell> {

		/**
		 * Number of rows that have already been added to the model
		 */
		private int count = 0;

		@Override
		protected Integer doInBackground() throws Exception {
			nextBtn.setEnabled(false);
			loadingLabel.setText("Processing query...");

			// Retrieve the next batch of results
			int count = 0;
			if (isExecuteIncrementally()) {
				ASGResult asgRes = result.getResult();
				if (asgRes != null) {
					while (!isCancelled() && asgRes.hasNext() && count < resultsShown) {
						Cell cell = (Cell) asgRes.next();
						publish(cell);
						count++;
					}
				}
			} else {
				@SuppressWarnings("unchecked")
				List<Cell> cells = ASG.fromASGList(result.getAsg());
				resultModel.setNumRows(cells.size());
				for (Cell c : cells) {
					publish(c);
					count++;
				}
			}

			return count;
		}

		@Override
		protected void process(List<Cell> chunks) {
			// Add the batch of results to the table model
			int numCols = 0;
			for (Cell cell : chunks) {
				if (cell.representsTuple() > -1) {
					@SuppressWarnings("unchecked")
					final ArrayList<Cell> cols = ASG.fromASGTuple(cell);
					int crtCol = 0;
					numCols = cols.size();
					resultModel.setColumnCount(numCols);

					for (Cell col : cols) {
						resultModel.setValueAt(col.toString(), count, crtCol);
						crtCol++;
					}
				} else {
					numCols = 1;
					resultModel.setColumnCount(1);
					resultModel.setValueAt(cell.toString(), count, 0);
				}

				count++;
			}

			String[] colNames = new String[numCols];
			Arrays.fill(colNames, "");
			resultModel.setColumnIdentifiers(colNames);
		}

		@Override
		protected void done() {
			super.done();
			if (loadingLabel != null) {
				nextBtn.setEnabled(true);
				loadingLabel.setText("");
			}

			Integer count;
			try {
				count = get();
				resultModel.setRowCount(count);
				if (count.intValue() == 0) {
					JOptionPane.showMessageDialog(resultsTable, "No more results to show!");
					nextBtn.setEnabled(false);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

	}

}
